package juc.popular;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 1. synchronized(悲观锁)可以用于修饰方法或代码块
 * * 修饰代码块时锁是后面括号里的对象
 * * 修饰实例方法时锁是调用方法的对象
 * * 修饰静态方法时锁是类对象
 * <p>
 * 2. synchronized(this)
 * * 锁的是当前对象,此时被synchronized锁住的代码块只在当前对象同步,如果有其他对象就不会同步了
 * * 由于this锁是锁住的当前对象,对于用不同对象new出来的Thread,不会受this锁的影响,也就不会实现同步
 * <p>
 * 3. synchronized(*.class)
 * * 对整个类进行加锁,在this的基础上还可以对同一类的不同对象进行同步
 * <p>
 * 4. volatile和synchronized的区别
 * * volatile本质是在告诉jvm当前变量在寄存器（工作内存）中的值是不确定的,需要从主存中读取;  synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住
 * * volatile仅能使用在变量级别; synchronized则可以使用在变量、方法、和类级别的
 * * volatile仅能实现变量的修改可见性,不能保证原子性; 而synchronized则可以保证变量的修改可见性和原子性
 * * volatile不会造成线程的阻塞; synchronized可能会造成线程的阻塞
 * * volatile标记的变量不会被编译器优化; synchronized标记的变量可以被编译器优化
 * 5. 作业练习
 * * 3个小朋友随机去拿20个苹果,A每次拿2个,B每次拿3个,C每次拿1个,
 * * 每人每次获取后都需要等2秒才能进行下一次获取,直到当前苹果苹果不够拿
 * * 例：假如剩1个苹果,然后A去拿,因为A每次需要拿2个,所以视为不够拿
 * <p>
 *
 * @author 王霄
 * @date 2021/9/19 22:09
 */
public class SynchronizedDemo {

    public static void main(String[] args) {
        AtomicInteger apple = new AtomicInteger(20);
        new Child("A" , apple, 2).start();
        new Child("B" , apple, 3).start();
        new Child("C" , apple, 1).start();
    }


    public static class Child extends Thread {
        private final AtomicInteger apple;
        private final int count;

        public Child(String name, AtomicInteger apple, int count) {
            super(name);
            this.apple = apple;
            this.count = count;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    if (apple.addAndGet(-count) >= 0) {
                        System.out.printf("%s拿了%d个苹果%n" , getName(), count);
                        try {
                            TimeUnit.SECONDS.sleep(2);
                        } catch (InterruptedException ignored) {
                        }
                    } else {
                        System.out.printf("%s不拿了退出游戏%n" , getName());
                        break;
                    }
                }
            }
        }
    }
}
